

inline char* RLEOutputLiteral(char* output, const char* buffer, size_t size)
{
  short* outshort = (short*)output;
  *outshort = -((short)(size));
  output +=2;

  memcpy(output, buffer, size);
  output += size;
  return output;
}


inline size_t RLECompress(const char* in, size_t inlen, char* out)
{
  char* startOut = out;
  const char* start_range = in;
  const char* end_range = in;

  for (size_t i =0; i<inlen-4; i++)
  {
    if (end_range - in >= (ptrdiff_t)inlen) break;

    if (end_range-start_range == ptrdiff_t((1<<15)-1))
    {
      out = RLEOutputLiteral(out, start_range, end_range-start_range);
      start_range = end_range;
    } else
      if (end_range[0] == end_range[1] && end_range[1] == end_range[2] && end_range[2] == end_range[3])
      {
        if (end_range-start_range > 0)
        {
          out = RLEOutputLiteral(out, start_range, end_range-start_range);
          start_range = end_range;
        }

        end_range += 3;
        char look = *end_range;
        while (end_range-in < (ptrdiff_t)inlen && end_range-start_range < ptrdiff_t((1<<15)-1) && *end_range == look)
          end_range++;

        short* outshort = (short*)out;
        *outshort = (short)(end_range-start_range);
        out += 2;
        *out = look;
        out++;

        start_range = end_range;
      } else
      {
        end_range++;
      }
  }

  if (end_range-start_range > 0)
  {
    out = RLEOutputLiteral(out, start_range, end_range-start_range);
    start_range = end_range;
  }


  return out-startOut;
}

inline size_t RLECalculateCompressedSize(const char* in, size_t inlen)
{
  const char* start_range = in;
  const char* end_range = in;
  size_t result = 0;

  for (size_t i =0; i<inlen-4; i++)
  {
    if (end_range - in >= (ptrdiff_t)inlen) break;

    if (end_range-start_range == ptrdiff_t((1<<15)-1))
    {
      result += 2;
      result += end_range-start_range;
      start_range = end_range;
    } else
      if (end_range[0] == end_range[1] && end_range[1] == end_range[2] && end_range[2] == end_range[3])
      {
        if (end_range-start_range > 0)
        {
          result += 2;
          result += end_range-start_range;
          start_range = end_range;
        }

        end_range += 3;
        char look = *end_range;
        while (end_range-in < (ptrdiff_t)inlen && end_range-start_range < ptrdiff_t((1<<15)-1) && *end_range == look)
          end_range++;

        result += 3;
        start_range = end_range;
      } else
      {
        end_range++;
      }
  }

  if (end_range-start_range > 0)
  {
    result += 2;
    result += end_range-start_range;
    start_range = end_range;
  }


  return result;
}

inline void ForEachRLECompressedByte(const char* buf, size_t size, void(*func)(char,size_t,void*), void* userdata)
{
  size_t curPos = 0;
  const char* startIn = buf;
  while (buf-startIn < (ptrdiff_t)size)
  {
    short desc = *((short*)buf);
    buf += 2;

    if (desc < 0)
    {
      desc = -desc;
      for (short i=0; i<desc; i++)
        func(buf[i], curPos+i, userdata);
      curPos += desc;
      buf += desc;
    } else
    {
      for (short i=0; i<desc; i++)
        func(*buf, curPos+i, userdata);
      curPos += desc;
      buf++;
    }
  }
}

inline size_t RLECalculateDecompressedSize(const char* in, size_t inlen)
{
  const char* startIn = in;
  size_t result = 0;

  while (in-startIn < (ptrdiff_t)inlen)
  {
    short desc = *((short*)in);
    in += 2;

    if (desc < 0)
    {
      desc = -desc;
      result += desc;
      in += desc;
    } else
    {
      result += desc;
      in++;
    }
  }

  return result;
}

inline size_t RLEDecompress(const char* in, size_t inlen, char* out)
{
  const char* startOut = out;
  const char* startIn = in;
  while (in-startIn < (ptrdiff_t)inlen)
  {
    short desc = *((short*)in);
    in += 2;

    if (desc < 0)
    {
      desc = -desc;
      memcpy(out, in, desc);
      out += desc;
      in += desc;
    } else
    {
      memset(out, *in, desc);
      out += desc;
      in++;
    }
  }

  return out-startOut;
}

inline void DeltaEncodeInPlace(char* buf, size_t size)
{
  int prev = 0;
  for (size_t i =0; i<size; i++)
  {
    int nprev = buf[i];
    buf[i] = (char)((512+(nprev-prev))%256);
    prev = nprev;
  }
}